package com.txw.javaCore.server.service;

import cn.hutool.core.lang.Snowflake;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.txw.javaCore.api.enums.Constant;
import com.txw.javaCore.model.entity.Item;
import com.txw.javaCore.model.mapper.ItemMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.*;

/**
 * @PROJECT_NAME: JavaCoreB2
 * @DESCRIPTION:
 * @Author: 涂玄武
 * @DATE: 2020/12/25 11:10
 */
@Component
public class BatchDataService {

    private static final Logger log = LoggerFactory.getLogger(BatchDataService.class);

    private static final BlockingQueue BLOCKING_QUEUE = new ArrayBlockingQueue(8);

    private static final ThreadPoolExecutor.CallerRunsPolicy POLICY = new ThreadPoolExecutor.CallerRunsPolicy();

    private static final ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(4, 8, 60, TimeUnit.SECONDS, BLOCKING_QUEUE, POLICY);

    @Autowired
    private ItemMapper itemMapper;

    private static final Snowflake SNOWFLAKE = new Snowflake(2, 3);

    private static final Integer sheetTotal = 65535;

    /**
     * 批量预生成并插入数据-多线程
     *
     * @param times
     * @throws Exception
     */
    public void batchInitItem(Integer times) throws Exception {
        List<BatchCallable> list = Lists.newLinkedList();
        for (int i = 1; i <= times; i++) {
            list.add(new BatchCallable(Constant.batchDataSize));
        }
        EXECUTOR.invokeAll(list);
    }

    class BatchCallable implements Callable<Boolean> {
        private Long total;

        public BatchCallable(Long total) {
            this.total = total;
        }

        /**
         * 真正的实现批量插入数据的代码逻辑
         *
         * @return
         * @throws Exception
         */
        @Override
        public Boolean call() throws Exception {
            try {
                List<Item> items = Lists.newLinkedList();
                Long i = 0L;
                for (; i < total; i++) {
                    items.add(new Item(SNOWFLAKE.nextIdStr(), SNOWFLAKE.nextIdStr()));
                }
                itemMapper.insertBatch(items);
            } catch (Exception e) {
                log.error("异常：", e);
            }
            return true;
        }
    }

    //POI批量查询获取数据-优化
    public List<Item> getDataPOI(final String search) throws Exception {
        List<Item> resList = Lists.newLinkedList();

        //总体思路：获取待导出数据的总数total，由于每个工作表塞入65535，因此65535可以当做是分页中pageSize，即每页需要查询获取的数据量，
        //         total/65535即可得出总页数，然后为 “每一页获取65535条数据” 分配一个task，并发交给多线程去异步执行
        Long total = itemMapper.countAll(search);
        if (total > sheetTotal) {
            Long count = (total % sheetTotal == 0) ? total / sheetTotal : total / sheetTotal + 1;

            List<PoiDataCallable> calls = Lists.newLinkedList();
            for (int i = 1; i <= count; i++) {
                calls.add(new PoiDataCallable(i, search));
            }
            List<Future<List<Item>>> list = EXECUTOR.invokeAll(calls);
            for (Future<List<Item>> future : list) {
                resList.addAll(future.get());
            }
        } else {
            resList = itemMapper.selectAll(search);
        }
        return resList;
    }

    class PoiDataCallable implements Callable<List<Item>> {
        private Integer pageNo;
        private String search;

        public PoiDataCallable(Integer pageNo, String search) {
            this.pageNo = pageNo;
            this.search = search;
        }

        @Override
        public List<Item> call() throws Exception {
            log.info("----当前线程：{}", Thread.currentThread().getName());

            PageHelper.startPage(pageNo, sheetTotal);
            return itemMapper.selectAll(search);
        }
    }

    /**
     * 获取导出Excel需要的数据-easyExcel
     * @param search
     * @return
     * @throws Exception
     */
    public List<List<Item>> getDataV2(final String search) throws Exception{
        List<List<Item>> resList = Lists.newLinkedList();

        Long total = itemMapper.countAll(search);
        if (total > sheetTotal){
            Long count = (total % sheetTotal == 0) ? total / sheetTotal : total / sheetTotal + 1;
            List<PoiDataCallable> calls = Lists.newLinkedList();
            for (int i = 0; i <= count; i++) {
                calls.add(new PoiDataCallable(i,search));
            }
            List<Future<List<Item>>> list = EXECUTOR.invokeAll(calls);
            for (Future<List<Item>> future:list) {
                resList.add(future.get());
            }
        }else {
            resList.add(itemMapper.selectAll(search));
        }
        return resList;
    }

}
